/*
* @Author: simon.yang
* @Date:   2021-11-29 19:43:31
* @Last Modified by:   simon.yang
* @Last Modified time: 2021-11-29 20:40:34
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <semaphore.h>
#include <time.h>
 
// Max Chirs Number
#define MAX_CHAIRS 5
#define TRUE 1
// sem_wait
#define p(x) sem_wait(&x)
// sem_post
#define v(x) sem_post(&x)
 
// init student and TA
sem_t student,TA;
// init chairs
int chairs;
pthread_mutex_t mutex;


// init student and TA and chairs
void init ( )
{
    sem_init ( &student , 0 , 0 );
    sem_init ( &TA , 0 , 1 );
    chairs = MAX_CHAIRS;
}

// TA function
void* _TA ( )
{
    while ( TRUE )
    {
        p(student);
        pthread_mutex_lock(&mutex);
        chairs++;
        pthread_mutex_unlock(&mutex);
        // one work done
        sleep(5);
        printf ( "One work done....\n" );
        v(TA);
    }
}
 
// student function
void* _student ( void* arg )
{
    int *p = (int*) arg;
    int x = *p;
    pthread_mutex_lock(&mutex);
    int random_sleep_time = rand() % 10;
    if ( chairs > 0 )
    {
        chairs--;
        // student function
        v(student);
        printf ( "Student %d hanging out for programming for %d seconds\n" , x , random_sleep_time );
        printf("Totally now %d students wating...\n", MAX_CHAIRS - chairs );
        pthread_mutex_unlock(&mutex);
        p(TA);

    }
    else
    {
        // that thread is done 
        pthread_mutex_unlock(&mutex);
        printf ( "The %dth student go away!\n" , x );
    }
}
 
int main ( )
{
    int i;
    init ( );
    pthread_t b;
    // create threads
    pthread_t pthreads[MAX_CHAIRS*100];
    int cid[MAX_CHAIRS*100];
    pthread_create ( &b , NULL , _TA , NULL );
    for ( i = 0 ; i < MAX_CHAIRS*100 ; i ++ )
    {
        // in loop doing student
        sleep(1);
        cid[i] = i;
        pthread_create ( &pthreads[i] , NULL , _student , &cid[i] );
    }
    pthread_join ( b , NULL );
    for ( i = 0 ; i < MAX_CHAIRS*100 ; i++ )
        // join thread
        pthread_join ( cid[i] , NULL );
}
